﻿using Red.Controls;
using Red.Controls.ViewModels;
using Red.EngineData;
using Red.EngineData.View;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace Red.Toolkit.PresentationEditor.ViewModels
{

    public abstract class PresentationTreeItem : RedTreeItem
    {
        protected readonly DataView _view;
        protected readonly SceneTree _tree;

        public PresentationTreeItem( SceneTree tree, string title, DataView view, Canvas image, PresentationTreeItem parent )
            : base( parent, title, "", image )
        {
            _view = view;
            _tree = tree;
        }

        public DataView View
        {
            get { return _view; }
        }

        public virtual bool IsNodeVisible
        {
            get
            {
                return true;
            }
        }
    }

    public class PresentationTreeItemRoot : PresentationTreeItem
    {
        public PresentationTreeItemRoot( SceneTree tree, DataView view )
            : base( tree, "__Root", view, null, null )
        {
            IsExpanded = true;
        }

        protected override void EnumerateChildren( ICollection<RedTreeItem> children )
        {
            if ( children.Count == 0 )
                children.Add( new PrefabTreeItemPrefab( _tree, _view ) );
        }
    }

    public class PresentationTreeItemPrefab : PresentationTreeItem
    {
        protected static readonly Canvas _presentationIcon;

        private ArrayDataEnumerator _nodeEnumerator;

        private Notifier _onNodeAdded;
        private Notifier _onNodeRemoved;

        protected SortedSet<string> _nodeIds;
        protected bool _hasError;

        public PresentationTreeItemPrefab( SceneTree tree, DataView view )
            : base( tree, "<presentation>", view, _presentationIcon, null )
        {
            _hasError = false;
            _nodeIds = new SortedSet<String>();

            // get presentation name
            _view.Execute( Interop.Make( "name" ), result => Title = result.Data, ex => _hasError = true );

            // bind notification callbacks
            _view.RequestNotifier( "_onNodeAdded", notifier =>
            {
                _onNodeAdded = notifier;
                _onNodeAdded.OnNotifyEvent += NodeAdded;
            } );
            _view.RequestNotifier( "_onNodeRemoved", notifier =>
            {
                _onNodeRemoved = notifier;
                _onNodeRemoved.OnNotifyEvent += NodeRemoved;
            } );

            // prepare node enumeration
            _nodeEnumerator = new ArrayDataEnumerator( _tree.Dispatcher, _view.GetSubView( "nodeIds" ) );
            _nodeEnumerator.OnEnumerationFailed += NodeEnumerationFailed;
            _nodeEnumerator.OnEnumerationDone += NodeEnumerationDone;
            _nodeEnumerator.Start();

            // expand
            IsExpanded = true;
        }

        static PresentationTreeItemPrefab()
        {
            _presentationIcon = (Canvas)Application.Current.FindResource( "appbar_globe" );
        }

        private void NodeEnumerationFailed()
        {
            _hasError = true;
        }

        private void NodeEnumerationDone( List<string> nodeIds )
        {
            _hasError = false;

            foreach ( var id in nodeIds )
                _nodeIds.Add( id );

            Rebuild();
        }

        private void NodeAdded( string nodeId )
        {
            if ( !_nodeIds.Contains( nodeId ) )
            {
                _nodeIds.Add( nodeId );
                Rebuild();
            }
        }

        private void NodeRemoved( string nodeId )
        {
            if ( _nodeIds.Contains( nodeId ) )
            {
                _nodeIds.Remove( nodeId );
                Rebuild();
            }
        }

        protected override void EnumerateChildren( ICollection<RedTreeItem> children )
        {
            // map current children
            // TODO: we should create a sub class of the RedTreeItem to support this lazy rebuilding functionality better
            var nodeMap = new Dictionary<string, PresentationTreeItemNode>();
            foreach ( PresentationTreeItemNode a in children )
            {
                nodeMap.Add( a.NodeID, a );
            }

            // add missing nodes
            foreach ( var nodeId in _nodeIds )
            {
                // try to reuse existing item
                PresentationTreeItemNode nodeItem = null;
                nodeMap.TryGetValue( nodeId, out nodeItem );

                if ( nodeItem != null )
                {
                    nodeMap.Remove( nodeId );
                }
                else
                {
                    var nodeView = _view.GetSubView( string.Format( "nodes[\"{0}\"]", nodeId ) );
                    nodeItem = new PresentationTreeItemNode( _tree, nodeView, nodeId );
                    children.Add( nodeItem );
                }
            }

            // delete elements that are no longer used
            foreach ( var node in nodeMap.Values )
                children.Remove( node );
        }
    }

    public class PresentationTreeItemNode : PresentationTreeItem
    {
        protected static readonly Canvas _nodeIcon;

        private readonly string _nodeId;
        private bool _isNodeVisible;

        public string NodeID
        {
            get
            {
                return _nodeId;
            }
        }

        public string PresentationPath { get; set; }

        public override bool IsNodeVisible
        {
            get
            {
                return _isNodeVisible;
            }
        }


        public static int CompareName( PresentationTreeItemNode c1, PresentationTreeItemNode c2 )
        {
            return c1.Title.CompareTo( c2.Title );
        }

        public PresentationTreeItemNode( SceneTree tree, DataView view, string nodeId )
            : base( tree, "<node>", view, _nodeIcon, null )
        {
            _nodeId = nodeId;
            _isNodeVisible = true;

            IsExpanded = true;

            // get presentation name
            _view.Execute( Interop.Make( "name" ), result => Title = result.Data, (Action<Exception>)null );
            _view.Execute( Interop.Make( "presentationPath" ), result => PresentationPath = result.Data, (Action<Exception>)null );
        }

        static PresentationTreeItemNode()
        {
            _nodeIcon = (Canvas)Application.Current.FindResource( "appbar_cards_diamond" );
        }

        public override IEnumerable<CommandWrapper> Commands
        {
            get
            {
                List<CommandWrapper> commands = new List<CommandWrapper>();
                commands.Add( new CommandWrapper( PrefabEditorCommands.MakeUnique, this ) );
                return commands;
            }
        }

        protected override void EnumerateChildren( ICollection<RedTreeItem> children )
        {
        }
    }

}
